﻿using DNFImagePacks2Manager.Models;
using SharpCompress.Archives;
using SharpCompress.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace DNFImagePacks2Manager.Helpers
{
    // 压缩包助手
    public static class ArchiveHelper
    {

        // 是否是压缩文件
        public static bool IsArchiveFile(string fileName)
        {
            return fileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) ||
                   fileName.EndsWith(".rar", StringComparison.OrdinalIgnoreCase) ||
                   fileName.EndsWith(".7z", StringComparison.OrdinalIgnoreCase);
        }

        // 解压文件
        public static int ExtractArchive(string archivePath, Action<string, string>? progressCallback, string? password = null)
        {
            try
            {
                using var archive = ArchiveFactory.Open(archivePath, new()
                {
                    Password = password ?? string.Empty,
                    ArchiveEncoding = new ArchiveEncoding
                    {
                        Default = Encoding.UTF8,
                        Forced = Encoding.UTF8
                    },
                    LookForHeader = true
                });
                string archiveName = Path.GetFileNameWithoutExtension(archivePath);
                IEnumerable<IArchiveEntry> entries = archive.Entries;
                int currentCount = 0;
                int totalCount = entries.Count();
                foreach (var entry in entries)
                {
                    currentCount++;

                    if (entry.Key == null || entry.IsDirectory)
                        continue;

                    string fileName = Path.GetFileName(entry.Key);

                    string progressInfo = $"{currentCount}/{totalCount}";
                    progressCallback?.Invoke("解压进度", progressInfo);
                    progressCallback?.Invoke("当前文件", fileName);

                    if (fileName.EndsWith(".csm", StringComparison.OrdinalIgnoreCase))
                    {
                        int status = ExtractCustom(entry, fileName, out string message);
                        progressCallback?.Invoke("日志", message);
                        if (status == 2)
                        {
                            return status;
                        }
                        continue;
                    }

                    if (fileName.EndsWith(".csm2", StringComparison.OrdinalIgnoreCase))
                    {
                        int status = ExtractCustom(entry, fileName, out string message);
                        progressCallback?.Invoke("日志", message);
                        if (status == 2)
                        {
                            return status;
                        }
                        continue;
                    }

                    if (fileName.EndsWith(".bk2", StringComparison.OrdinalIgnoreCase))
                    {
                        int status = ExtractBKFile(entry, archiveName, entry.Key, out string message);
                        progressCallback?.Invoke("日志", message);
                        if (status == 2)
                        {
                            return status;
                        }
                        continue;
                    }

                    if (NPKHelper.FilenameCheckNPKFile(fileName))
                    {
                        int status = ExtractNPKFile(entry, fileName, out string messsage);
                        progressCallback?.Invoke("日志", messsage);
                        if (status == 2)
                        {
                            return status;
                        }
                        continue;
                    }

                    if (ArchiveHelper.IsArchiveFile(fileName))
                    {
                        progressCallback?.Invoke("日志", $"→ {fileName} 检测到嵌套压缩包，已跳过");
                        continue;
                    }

                    progressCallback?.Invoke("日志", $"× {fileName} 无效的文件类型");
                }

                return 1;
            }
            catch (CryptographicException)
            {
                progressCallback?.Invoke("日志", "× 密码错误或不支持的加密算法（如 ZIP AES 加密）");
                return 2;
            }
            catch (InvalidFormatException)
            {
                progressCallback?.Invoke("日志", "× 压缩包格式无效（可能是 RAR5 或损坏）");
                return 0;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        public static int ExtractCustom(IArchiveEntry entry, string fileName, out string message)
        {
            int status = 0;
            string name = Path.GetFileNameWithoutExtension(fileName);

            try
            {
                string customFolder = CustomHelper.GetCustomFolder();
                string customFile = Path.Combine(customFolder, fileName);

                if (File.Exists(customFile))
                {
                    message = $"× 个性化配置 {name} 已存在，已跳过";
                    return -1;
                }
                status = ExtractToFile(entry, customFile);
                if (status==1)
                {
                    message = $"√ 个性化配置 {name} 导入成功";
                }
                else
                {
                    if (File.Exists(customFile))
                    {
                        File.Delete(customFile);
                    }
                    message = $"× 个性化配置 {name} 导入失败";
                }
            }
            catch (Exception ex)
            {
                message = $"× 个性化配置 {name} 导入失败: {ex.Message}";
            }
            return status;
        }

        public static int ExtractBKFile(IArchiveEntry entry, string archiveName, string path, out string message)
        {
            int status = 0;
            string[] pathSegments = path.Split(
                [Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar],
                StringSplitOptions.RemoveEmptyEntries
            );
            if (pathSegments.Length > 0 &&
               string.Equals(pathSegments[0], "Video", StringComparison.OrdinalIgnoreCase))
            {
                string fileName = Path.GetFileName(path);

                try
                {
                    string bkFolder = Path.Combine(BKHelper.GetWholeFolder(), archiveName);
                    string[] remainingSegments = pathSegments.Skip(1).ToArray();
                    string relativePath = remainingSegments.Length > 0
                    ? string.Join(Path.DirectorySeparatorChar.ToString(), remainingSegments)
                    : "";
                    string targetFile = Path.Combine(bkFolder, relativePath);
                    string? targetFolder = Path.GetDirectoryName(targetFile);
                    if (targetFolder != null && !Directory.Exists(targetFolder))
                    {
                        Directory.CreateDirectory(targetFolder);
                    }
                    if (File.Exists(targetFile))
                    {
                        message = $"× BK文件 {fileName} 已存在，已跳过";
                        return -1;
                    }
                    status = ExtractToFile(entry, targetFile);
                    if (status == 1)
                    {
                        message = $"√ BK文件 {fileName} 导入成功";
                    }
                    else
                    {
                        if (File.Exists(targetFile))
                        {
                            File.Delete(targetFile);
                        }
                        message = $"× BK文件 {fileName} 导入失败";
                    }
                }
                catch (Exception ex) {
                    message = $"× BK文件 {fileName} 导入失败: {ex.Message}";
                }
            }
            else
            {
                string fileName = Path.GetFileName(path);

                try
                {
                    string folder = BKHelper.GetSingleFolder();
                    string targetFile = Path.Combine(folder, fileName);
                    if (File.Exists(targetFile))
                    {
                        message = $"× BK文件 {fileName} 已存在，已跳过";
                        return -1;
                    }
                    status = ExtractToFile(entry, targetFile);
                    if (status == 1)
                    {
                        message = $"√ BK文件 {fileName} 导入成功";
                    }
                    else
                    {
                        if (File.Exists(targetFile))
                        {
                            File.Delete(targetFile);
                        }
                        message = $"× BK文件 {fileName} 导入失败";
                    }
                }
                catch (Exception ex)
                {
                    message = $"× BK文件 {fileName} 导入失败: {ex.Message}";
                }
            }
            return status;
        }

        public static int ExtractNPKFile(IArchiveEntry entry, string fileName, out string message)
        {
            int status = 0;
            string name = Path.GetFileNameWithoutExtension(fileName);

            try
            {

                NPKItemBase npkItem;

                int type = GetNPKType(entry, out int typeStatus);
                if (typeStatus == 2)
                {
                    message = $"× 补丁类型判断失败，需要密码 {fileName}";
                    return 2;
                }

                switch (type)
                {
                    case 2:
                        npkItem = new NPKItemSound(fileName);
                        break;
                    case 1:
                        npkItem = new NPKItemImage(fileName);
                        break;
                    case 0:
                    default:
                        if (name.Contains("sound"))
                        {
                            npkItem = new NPKItemSound(fileName);
                        }
                        else
                        {
                            npkItem = new NPKItemImage(fileName);
                        }
                        break;
                }

                string display = npkItem.DisplayName;

                if (npkItem.IsFileExist)
                {
                    message = $"× 补丁文件 {display} 已存在，已跳过";
                    return -1;
                }
                string npkFile = npkItem.EnableFile;
                status = ExtractToFile(entry, npkFile);
                if (status==1)
                {
                    message = $"√ 补丁文件 {display} 安装成功";
                }
                else
                {
                    if (File.Exists(npkFile))
                    {
                        File.Delete(npkFile);
                    }
                    message = $"× 补丁文件 {display} 安装失败";
                }
            }
            catch (Exception ex)
            {
                message = $"× 补丁文件 {name} 安装失败: {ex.Message}";
            }
            return status;
        }

        public static int GetNPKType(IArchiveEntry entry, out int status)
        {
            int type = 0;
            try
            {
                byte[] entryData;
                using (var stream = entry.OpenEntryStream())
                {
                    using var memoryStream = new MemoryStream();
                    stream.CopyTo(memoryStream);
                    entryData = memoryStream.ToArray();
                }
                using (var typeCheckStream = new MemoryStream(entryData))
                {
                    type = NPKHelper.GetNPKType(typeCheckStream);
                }
                status = 1;
            }
            catch (CryptographicException)
            {
                status = 2;
            }
            catch (Exception ex)
            {
                status = 0;
                if ("SharpCompress.Compressors.LZMA.DataErrorException".Equals(ex.GetType().FullName))
                {
                    status = 2;
                }
            }
            return type;
        }

        public static int ExtractToFile(IArchiveEntry entry, string file)
        {
            try
            {
                entry.WriteToFile(file);
                return 1;
            }
            catch (CryptographicException)
            {
                return 2;
            }
            catch (Exception ex)
            {
                if ("SharpCompress.Compressors.LZMA.DataErrorException".Equals(ex.GetType().FullName))
                {
                    return 2;
                }
                return 0;
            }
        }
    }
}